<?php

class Kd_Object implements ArrayAccess {

    protected static $underscoreNames = array();
    protected static $wordNames = array();
    protected $data = array();

    public function __construct($data = array()) {
        $this->addData(is_array($data) ? $data : array());
    }

    public function getAllData() {
        return $this->data;
    }

    public function addData(array $values) {
        foreach ($values as $k => $v) {
            $this->setData($k, $v);
        }
        return $this;
    }

    public function getData($name, $default = NULL) {
        $methodName = $this->getMethodName($name, 'get');
        if ($methodName) {
            return $this->$methodName($default);
        }
        if ($this->hasData($name)) {
            return $this->data[$name];
        }
        return $default;
    }

    public function setData($name, $value) {
        $methodName = $this->getMethodName($name, 'set');
        if ($methodName) {
            return $this->$methodName($value);
        }
        $this->data[$name] = $value;
        return $this;
    }

    public function hasData($name) {
        $methodName = $this->getMethodName($name, 'has');
        if ($methodName) {
            return $this->$methodName();
        }
        return array_key_exists($name, $this->data);
    }

    public function issetData($name) {
        return isset($this->data[$name]) && !empty($this->data[$name]);
    }

    public function isArrayData($name){
        if($this->hasData($name)){
            return is_array($this->data[$name]);
        }
        return FALSE;
    }

    public function isEmptyArray($name){
        if($this->isArrayData($name)){
            return count($this->getData($name)) == 0;
        }
        return TRUE;
    }

    public function unsetData($name) {
        unset($this->data[$name]);
        return $this;
    }

    public function unsetAllData(){
        $this->data = array();
        return $this;
    }

    public function __get($name) {
        return $this->getData($this->toUnderline($name));
    }

    public function __set($name, $value) {
        $this->setData($this->toUnderline($name), $value);
    }

    public function __isset($name) {
        return $this->issetData($name);
    }

    public function __unset($name) {
        $this->unsetData($name);
    }

    public function __call($name, $arguments) {
        $methods = array(
            substr($name, 0, 3),
            $this->toUnderline(substr($name, 3))
        );
        if ($methods[0] == 'get') {
            return $this->getData($methods[1], isset($arguments[0]) ? $arguments[0] : NULL);
        } elseif ($methods[0] == 'set') {
            return $this->setData($methods[1], isset($arguments[0]) ? $arguments[0] : NULL);
        } elseif ($methods[0] == 'has') {
            return $this->hasData($methods[1]);
        }
    }

    public function offsetExists($offset) {
        return $this->hasData($offset);
    }

    public function offsetGet($offset) {
        return $this->getData($offset);
    }

    public function offsetSet($offset, $value) {
        $this->setData($offset, $value);
    }

    public function offsetUnset($offset) {
        $this->unsetData($offset);
    }

    public function toArray() {
        $result = array();
        foreach ($this->getAllData() as $name => $value) {
            if (is_object($value)) {
                $result[$name] = method_exists($value, 'toArray') ?
                        $value->toArray() :
                        $value;
            } else {
                $result[$name] = $value;
            }
        }
        return $result;
    }

    protected function getMethodName($name, $type) {
        if (!array_key_exists($name, self::$wordNames)) {
            self::$wordNames[$name] = Kd_Util_String::underlineToWord($name);
        }
        $methodName = $type . self::$wordNames[$name];
        if (method_exists($this, $methodName)) {
            return $methodName;
        }
        return NULL;
    }

    protected function toUnderline($name) {
        if (!array_key_exists($name, self::$underscoreNames)) {
            self::$underscoreNames[$name] = Kd_Util_String::wordToUnderline($name);
        }
        return self::$underscoreNames[$name];
    }

}